Setup
Load libraries
library(ggplot2)
library(tidyr)
library(dplyr)
library(Matrix)
library(Seurat)
library(cowplot)
library(patchwork)
# parallelization
library(future)
options(future.globals.maxSize= +Inf)
plan()
sequential:
- args: function (expr, envir = parent.frame(), substitute = TRUE, lazy = FALSE, seed = NULL, globals = TRUE, local = TRUE, earlySignal = FALSE, label = NULL, ...)
- tweaked: FALSE
- call: NULL
Process Human Data
import_remote_data <- function(file_url, type = "table", header = FALSE) {
con <- gzcon(url(file_url))
txt <- readLines(con)
if (type == "MM") { return (readMM(textConnection(txt))) }
if (type == "table") { return (read.table(textConnection(txt), header = header)) }
}
count_matrix_URL <- "https://ftp.ncbi.nlm.nih.gov/geo/series/GSE137nnn/GSE137537/suppl/GSE137537_counts.mtx.gz"
gene_names_URL <- "https://ftp.ncbi.nlm.nih.gov/geo/series/GSE137nnn/GSE137537/suppl/GSE137537_gene_names.txt.gz"
sample_annotations_URL <- "https://ftp.ncbi.nlm.nih.gov/geo/series/GSE137nnn/GSE137537/suppl/GSE137537_sample_annotations.tsv.gz"
human.count_matrix <- as.matrix(import_remote_data(count_matrix_URL, type = "MM"))
human.gene_names <- import_remote_data(gene_names_URL, type = "table")
human.sample_annotations <- import_remote_data(sample_annotations_URL, type = "table", header = TRUE)
human_ret_seurat
An object of class Seurat
19712 features across 20091 samples within 1 assay
Active assay: RNA (19712 features, 0 variable features)
Process Mouse Data
mouse.data <- Read10X(data.dir = "filtered_feature_bc_matrix")
dimnames(mouse.data)[[1]] <- tolower(dimnames(mouse.data)[[1]])
dimnames(mouse.data)[[2]] <- tolower(dimnames(mouse.data)[[2]])
mouse_ret_seurat <- CreateSeuratObject(counts = mouse.data,
project = "mouse_ret",
min.cells = 3,
min.features = 200)
mouse_ret_seurat
An object of class Seurat
16424 features across 4510 samples within 1 assay
Active assay: RNA (16424 features, 0 variable features)
Process Primate Data
url=https://ftp.ncbi.nlm.nih.gov/geo/series/GSE118nnn/GSE118546/suppl/GSE118546_macaque_fovea_all_10X_Jan2018.Rdata.gz
wget $url -O primate_data/GSE118546_macaque_fovea_all_10X_Jan2018.Rdata.gz
gunzip primate_data/*
install.packages( c('devtools', 'roxygen2') )
library(devtools)
library(roxygen2)
install_github( 'hb-gitified/cellrangerRkit',
auth_token = 'your_token' )
macaque_fovea_seurat
An object of class Seurat
30039 features across 111993 samples within 1 assay
Active assay: RNA (30039 features, 0 variable features)
Cleanup
rm(human.count_matrix, human.gene_names, human.sample_annotations)
rm(count_matrix_URL, gene_names_URL, sample_annotations_URL, import_remote_data)
rm(mouse.data)
rm(Count.mat_fovea, macaque_fovea)
Combine
# combine
ret.list <- list(human = human_ret_seurat, mouse = mouse_ret_seurat, macaque = macaque_fovea_seurat)
# preprocess
ret.list <- lapply(X = ret.list, FUN = function(x) {
x <- NormalizeData(x, verbose = FALSE)
x <- FindVariableFeatures(x, selection.method = "vst", nfeatures = 2000, verbose = FALSE)
})
# cleanup
rm(human_ret_seurat, mouse_ret_seurat, macaque_fovea_seurat)
Integration
plan("multiprocess", workers = 4)
ret.anchors <- FindIntegrationAnchors(object.list = ret.list, dims = 1:50, anchor.features = 1000)
plan("multiprocess", workers = 1)
ret.combined <- IntegrateData(anchorset = ret.anchors, dims = 1:50)
Integrated Analysis
plan("multiprocess", workers = 4)
DefaultAssay(ret.combined) <- "integrated"
# Run the standard workflow for visualization and clustering
ret.combined <- ScaleData(ret.combined, verbose = FALSE)
ret.combined <- RunPCA(ret.combined, npcs = 50, verbose = FALSE)
# t-SNE and Clustering
ret.combined <- RunUMAP(ret.combined, reduction = "pca", dims = 1:35)
ret.combined <- FindNeighbors(ret.combined, reduction = "pca", dims = 1:35)
ret.combined <- FindClusters(ret.combined, resolution = 0.075)
UMAP Visualization
DimPlot(ret.combined, reduction = "umap", group.by = "orig.ident")

DimPlot(ret.combined, reduction = "umap", label = TRUE)

DimPlot(ret.combined, reduction = "umap", split.by = "orig.ident", ncol = 1)

Identify Clusters with Canonical Markers
DefaultAssay(ret.combined) <- "RNA"
features <- tolower(c("Pde6a","Gnat2","Nefl","Camk2b","Thy1","Gad1","Slc6a9",
"Pcsk6","Trpm1","Sept4","Glul","Arr3","C1qa","Tm4sf1", "Mgp"))
FeaturePlot(object = ret.combined,
features = features,
pt.size = 0.1,
cols = c("lightgrey", "#F26969"),
min.cutoff = "q9",
combine = TRUE) & NoLegend() & NoAxes()

# Cowplot method: make sure to change to "combine = FALSE" and remove "& NoLegend() & NoAxes"
# for(i in 1:length(p)) {
# p[[i]] <- p[[i]] + NoLegend() + NoAxes()
# }
#
# cowplot::plot_grid(plotlist = p, ncol=3)
- Rod : pde6a
- AC (amacrine cell) : gad1, slc6a9
- MG (Müller glia) : glul
- BC (bipolar cell) : Trpm, camk2b
- CC (cone cell) : gnat2, arr3
- RGC (retinal ganglial cell) : nefl, thy1
- VC (vascular cell) : mgp, tm4sf1
- M (microglia) : c1qa
- HC (horizontal cell) : sept4
Markers were determined from this paper and other sources.

Find Differentially Expressed Genes
cells.types <- c("Rod", "BC", "MG", "RGC", "CC", "AC", "VC", "HC", "M")
theme_set(theme_cowplot())
cell_type_avg <- function(seurat.combined, ident) {
cells.x <- subset(seurat.combined, idents = ident)
Idents(cells.x) <- "orig.ident"
cells.x.avg <- log1p(AverageExpression(cells.x, verbose = FALSE)$RNA)
cells.x.avg$gene <- rownames(cells.x.avg)
return(cells.x.avg)
}
cells.plot <- as.list(cells.types)
cells.plot <- lapply(cells.plot, FUN = function(x) {
cells.x.avg <- cell_type_avg(ret.combined, ident = x)
x <- ggplot(cells.x.avg, aes(human_ret, mouse_ret)) + geom_point(size = 0.1) + ggtitle(x)
return(x)
})
# For individual plots
# for (p in cells.plot) {
# print(p)
# }
# For grid plot
cowplot::plot_grid(plotlist = cells.plot, ncol = 3)

ret.combined$celltype.organism <- paste(Idents(ret.combined), ret.combined$orig.ident, sep = "_")
ret.combined$celltype <- Idents(ret.combined)
Idents(ret.combined) <- "celltype.organism"
Tables with the most differentially expressed genes in each cell subtype:
for(i in seq_along(cells.diffgenes)) {
print(knitr::kable(head(cells.diffgenes[[i]]),caption=cells.types[[i]]))
}
Rod
| ckb |
0 |
1.4493770 |
0.918 |
0.724 |
0 |
Inf |
Rod |
ckb |
| hsp90aa1 |
0 |
1.3457646 |
0.854 |
0.627 |
0 |
Inf |
Rod |
hsp90aa1 |
| nrl |
0 |
1.3140138 |
0.874 |
0.635 |
0 |
Inf |
Rod |
nrl |
| 0610009b22rik |
0 |
-0.6622860 |
0.000 |
0.130 |
0 |
Inf |
Rod |
0610009b22rik |
| gm17018 |
0 |
-0.6831275 |
0.000 |
0.130 |
0 |
Inf |
Rod |
gm17018 |
| spata1 |
0 |
-0.6929677 |
0.000 |
0.132 |
0 |
Inf |
Rod |
spata1 |
BC
| neat1 |
0 |
3.086391 |
0.793 |
0.064 |
0 |
Inf |
BC |
neat1 |
| mtch1 |
0 |
-1.305054 |
0.000 |
0.459 |
0 |
Inf |
BC |
mtch1 |
| selenom |
0 |
-1.338108 |
0.000 |
0.480 |
0 |
Inf |
BC |
selenom |
| araf |
0 |
-1.342891 |
0.013 |
0.494 |
0 |
Inf |
BC |
araf |
| klc3 |
0 |
-1.424615 |
0.002 |
0.500 |
0 |
Inf |
BC |
klc3 |
| pea15a |
0 |
-1.427543 |
0.000 |
0.500 |
0 |
Inf |
BC |
pea15a |
MG
| tf |
0 |
5.089073 |
0.962 |
0.000 |
0 |
Inf |
MG |
tf |
| spp1 |
0 |
3.879036 |
0.847 |
0.003 |
0 |
Inf |
MG |
spp1 |
| crabp1 |
0 |
3.865908 |
0.876 |
0.028 |
0 |
Inf |
MG |
crabp1 |
| gpx3 |
0 |
3.736219 |
0.869 |
0.052 |
0 |
Inf |
MG |
gpx3 |
| ftl |
0 |
3.672007 |
0.877 |
0.000 |
0 |
Inf |
MG |
ftl |
| actg1 |
0 |
3.639157 |
0.905 |
0.026 |
0 |
Inf |
MG |
actg1 |
RGC
| malat1 |
0e+00 |
-5.358199 |
0.000 |
1.000 |
0.0000020 |
23.73532 |
RGC |
malat1 |
| gm42418 |
0e+00 |
-5.948091 |
0.000 |
0.957 |
0.0000083 |
22.29749 |
RGC |
gm42418 |
| ay036118 |
0e+00 |
-2.770196 |
0.000 |
0.826 |
0.0004565 |
18.29374 |
RGC |
ay036118 |
| neat1 |
0e+00 |
5.237230 |
0.889 |
0.087 |
0.0007506 |
17.79653 |
RGC |
neat1 |
| linc00599 |
1e-07 |
2.669694 |
0.815 |
0.000 |
0.0024960 |
16.59495 |
RGC |
linc00599 |
| kcnq1ot1 |
1e-07 |
2.290579 |
0.926 |
0.261 |
0.0025965 |
16.55548 |
RGC |
kcnq1ot1 |
CC
| gm42418 |
0 |
-5.444663 |
0.000 |
1.000 |
0 |
185.7925 |
CC |
gm42418 |
| malat1 |
0 |
-5.893437 |
0.000 |
1.000 |
0 |
185.7925 |
CC |
malat1 |
| ay036118 |
0 |
-2.515711 |
0.000 |
0.943 |
0 |
170.6009 |
CC |
ay036118 |
| gnat2 |
0 |
-2.832927 |
0.117 |
0.943 |
0 |
153.8304 |
CC |
gnat2 |
| mir124a-1hg |
0 |
-2.420164 |
0.000 |
0.869 |
0 |
151.9125 |
CC |
mir124a-1hg |
| gngt2 |
0 |
-3.274161 |
0.143 |
0.937 |
0 |
149.3963 |
CC |
gngt2 |
AC
| gm42418 |
0 |
-5.868449 |
0 |
1.000 |
0 |
258.1168 |
AC |
gm42418 |
| malat1 |
0 |
-6.139436 |
0 |
0.994 |
0 |
255.9481 |
AC |
malat1 |
| ay036118 |
0 |
-2.993976 |
0 |
0.935 |
0 |
236.7899 |
AC |
ay036118 |
| snhg11 |
0 |
-3.730037 |
0 |
0.916 |
0 |
230.5453 |
AC |
snhg11 |
| ac149090.1 |
0 |
-2.375670 |
0 |
0.729 |
0 |
173.6489 |
AC |
ac149090.1 |
| c230004f18rik |
0 |
-2.693746 |
0 |
0.729 |
0 |
173.6488 |
AC |
c230004f18rik |
VC
| hla-b |
0 |
3.429125 |
0.884 |
0.00 |
0 |
112.97262 |
VC |
hla-b |
| rps3a |
0 |
2.938618 |
0.826 |
0.00 |
0 |
104.06885 |
VC |
rps3a |
| hla-e |
0 |
3.220179 |
0.826 |
0.00 |
0 |
104.06878 |
VC |
hla-e |
| hla-a |
0 |
3.030967 |
0.812 |
0.00 |
0 |
101.88369 |
VC |
hla-a |
| hla-c |
0 |
2.913989 |
0.797 |
0.00 |
0 |
99.71476 |
VC |
hla-c |
| a2m |
0 |
3.322554 |
0.797 |
0.01 |
0 |
96.28914 |
VC |
a2m |
HC
| gm42418 |
0 |
-6.046691 |
0.000 |
1.000 |
0 |
108.45517 |
HC |
gm42418 |
| malat1 |
0 |
-5.904074 |
0.000 |
0.942 |
0 |
100.35094 |
HC |
malat1 |
| neat1 |
0 |
4.028649 |
0.967 |
0.043 |
0 |
70.98094 |
HC |
neat1 |
| ay036118 |
0 |
-2.922930 |
0.000 |
0.710 |
0 |
70.65422 |
HC |
ay036118 |
| pvalb |
0 |
3.437120 |
0.902 |
0.000 |
0 |
63.70192 |
HC |
pvalb |
| gpi1 |
0 |
-2.385417 |
0.000 |
0.580 |
0 |
55.70038 |
HC |
gpi1 |
M
| ftl |
0 |
4.612295 |
0.98 |
0 |
0 |
80.75030 |
M |
ftl |
| hla-dra |
0 |
4.664191 |
0.94 |
0 |
0 |
76.57279 |
M |
hla-dra |
| hla-a |
0 |
2.899218 |
0.94 |
0 |
0 |
76.57279 |
M |
hla-a |
| hla-drb1 |
0 |
4.096260 |
0.92 |
0 |
0 |
74.52028 |
M |
hla-drb1 |
| rps3a |
0 |
3.397725 |
0.92 |
0 |
0 |
74.52028 |
M |
rps3a |
| hla-b |
0 |
3.163581 |
0.92 |
0 |
0 |
74.52028 |
M |
hla-b |
Save as csv files
for(i in seq_along(cells.diffgenes)) {
write.csv(cells.diffgenes[[i]], sprintf("results/%d_%s.csv", i, cells.types[[i]]))
}
genes_to_plot <- 3
for (i in seq_along(cells.types)) {
print(FeaturePlot(object = ret.combined,
features = rownames(cells.diffgenes[[i]])[1:genes_to_plot],
split.by = "orig.ident",
max.cutoff = 3,
cols = c("grey", "red"),
pt.size = 0.07,
combine = TRUE,
label.size = 0.5
) + plot_annotation(title = cells.types[[i]]) & NoLegend() & NoAxes()
)
}









Check cell proportion for each species:
knitr::kable(prop.table(x = table(Idents(ret.combined), ret.combined@meta.data$orig.ident), margin = 2))
| 0 |
0.2627047 |
0.1535810 |
0.2875831 |
| 1 |
0.5498980 |
0.0758172 |
0.3164080 |
| 2 |
0.0001493 |
0.1855205 |
0.0002217 |
| 3 |
0.0009955 |
0.1458216 |
0.0035477 |
| 4 |
0.0531581 |
0.0808176 |
0.0838137 |
| 5 |
0.0114977 |
0.0783888 |
0.0388027 |
| 6 |
0.0406650 |
0.0552267 |
0.0569845 |
| 7 |
0.0187148 |
0.0577625 |
0.0343681 |
| 8 |
0.0484794 |
0.0443242 |
0.0917960 |
| 9 |
0.0001493 |
0.0342075 |
0.0000000 |
| 10 |
0.0000498 |
0.0232247 |
0.0004435 |
| 11 |
0.0076154 |
0.0156081 |
0.0152993 |
| 12 |
0.0000000 |
0.0117775 |
0.0000000 |
| 13 |
0.0034344 |
0.0089827 |
0.0436807 |
| 14 |
0.0000000 |
0.0109203 |
0.0000000 |
| 15 |
0.0000000 |
0.0101435 |
0.0008869 |
| 16 |
0.0024887 |
0.0039645 |
0.0261641 |
| 17 |
0.0000000 |
0.0039110 |
0.0000000 |
Gene Enrichment Analysis

plot_enrichment <- function(type = "Rod", other = "") {
file_path <- sprintf("enrich_data/%s_%s.txt", type, other)
x <- read.table(file_path, header=T, sep="\t", skip = 11)
colnames(x) <- gsub("upload_1..fold.Enrichment.", "Fold_Enrichment", colnames(x))
colnames(x) <- gsub("upload_1..FDR.", "FDR", colnames(x))
colnames(x) <- gsub("GO.biological.process.complete", "GO", colnames(x))
colnames(x) <- gsub("Homo.sapiens...REFLIST..20851.", "Count", colnames(x))
x$GO<- factor(x$GO, levels = x$GO[order(x$Fold_Enrichment, decreasing =F)])
x <- x[order(x$FDR),]
x <- x[1:10,]
g<- ggplot(data=x, aes(x=Fold_Enrichment, y=GO, colour=FDR)) +
geom_point(aes(size=Count)) +
theme_bw() +
theme(panel.background = element_rect(fill = NA) ,
axis.ticks.x=element_blank(),
axis.text.y = element_text(size = 12) ,
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()) +
labs(x = " ", y =" ") +
scale_colour_gradient(low = "red", high = "blue") +
ggtitle(type)
return(g)
}
for (type in cells.types) {
print(plot_enrichment(type = type, other= "top200"))
rm(type)
}









LS0tCnRpdGxlOiAiSW50ZWdyYXRpbmcgUHJpbWF0ZSBEYXRhIGludG8gQW5hbHlzaXMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQotLS0KIyBTZXR1cApMb2FkIGxpYnJhcmllcwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHBhdGNod29yaykKCiMgcGFyYWxsZWxpemF0aW9uCmxpYnJhcnkoZnV0dXJlKQpvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemU9ICtJbmYpCnBsYW4oKQpgYGAKUHJvY2VzcyBIdW1hbiBEYXRhCmBgYHtyfQppbXBvcnRfcmVtb3RlX2RhdGEgPC0gZnVuY3Rpb24oZmlsZV91cmwsIHR5cGUgPSAidGFibGUiLCBoZWFkZXIgPSBGQUxTRSkgewogIGNvbiA8LSBnemNvbih1cmwoZmlsZV91cmwpKQogIHR4dCA8LSByZWFkTGluZXMoY29uKQogIGlmICh0eXBlID09ICJNTSIpIHsgcmV0dXJuIChyZWFkTU0odGV4dENvbm5lY3Rpb24odHh0KSkpIH0KICBpZiAodHlwZSA9PSAidGFibGUiKSB7IHJldHVybiAocmVhZC50YWJsZSh0ZXh0Q29ubmVjdGlvbih0eHQpLCBoZWFkZXIgPSBoZWFkZXIpKSB9Cn0KY291bnRfbWF0cml4X1VSTCA8LSAiaHR0cHM6Ly9mdHAubmNiaS5ubG0ubmloLmdvdi9nZW8vc2VyaWVzL0dTRTEzN25ubi9HU0UxMzc1Mzcvc3VwcGwvR1NFMTM3NTM3X2NvdW50cy5tdHguZ3oiCmdlbmVfbmFtZXNfVVJMIDwtICJodHRwczovL2Z0cC5uY2JpLm5sbS5uaWguZ292L2dlby9zZXJpZXMvR1NFMTM3bm5uL0dTRTEzNzUzNy9zdXBwbC9HU0UxMzc1MzdfZ2VuZV9uYW1lcy50eHQuZ3oiCnNhbXBsZV9hbm5vdGF0aW9uc19VUkwgPC0gImh0dHBzOi8vZnRwLm5jYmkubmxtLm5paC5nb3YvZ2VvL3Nlcmllcy9HU0UxMzdubm4vR1NFMTM3NTM3L3N1cHBsL0dTRTEzNzUzN19zYW1wbGVfYW5ub3RhdGlvbnMudHN2Lmd6IgoKaHVtYW4uY291bnRfbWF0cml4IDwtIGFzLm1hdHJpeChpbXBvcnRfcmVtb3RlX2RhdGEoY291bnRfbWF0cml4X1VSTCwgdHlwZSA9ICJNTSIpKQpodW1hbi5nZW5lX25hbWVzIDwtIGltcG9ydF9yZW1vdGVfZGF0YShnZW5lX25hbWVzX1VSTCwgdHlwZSA9ICJ0YWJsZSIpCmh1bWFuLnNhbXBsZV9hbm5vdGF0aW9ucyA8LSBpbXBvcnRfcmVtb3RlX2RhdGEoc2FtcGxlX2Fubm90YXRpb25zX1VSTCwgdHlwZSA9ICJ0YWJsZSIsIGhlYWRlciA9IFRSVUUpCmBgYApgYGB7cn0Kcm93bmFtZXMoaHVtYW4uY291bnRfbWF0cml4KSA8LSB0b2xvd2VyKGh1bWFuLmdlbmVfbmFtZXNbLDFdKQpjb2xuYW1lcyhodW1hbi5jb3VudF9tYXRyaXgpIDwtIHRvbG93ZXIoaHVtYW4uc2FtcGxlX2Fubm90YXRpb25zWywxXSkKCmh1bWFuX3JldF9zZXVyYXQgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGh1bWFuLmNvdW50X21hdHJpeCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGEuZGF0YSA9IGh1bWFuLnNhbXBsZV9hbm5vdGF0aW9ucywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2plY3QgPSAiaHVtYW5fcmV0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jZWxscyA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZmVhdHVyZXMgPSAyMDApCmh1bWFuX3JldF9zZXVyYXQKYGBgCgpQcm9jZXNzIE1vdXNlIERhdGEKYGBge3J9Cm1vdXNlLmRhdGEgPC0gUmVhZDEwWChkYXRhLmRpciA9ICJmaWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeCIpCmRpbW5hbWVzKG1vdXNlLmRhdGEpW1sxXV0gPC0gdG9sb3dlcihkaW1uYW1lcyhtb3VzZS5kYXRhKVtbMV1dKQpkaW1uYW1lcyhtb3VzZS5kYXRhKVtbMl1dIDwtIHRvbG93ZXIoZGltbmFtZXMobW91c2UuZGF0YSlbWzJdXSkKbW91c2VfcmV0X3NldXJhdCA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gbW91c2UuZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2plY3QgPSAibW91c2VfcmV0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jZWxscyA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZmVhdHVyZXMgPSAyMDApCm1vdXNlX3JldF9zZXVyYXQKYGBgCgpQcm9jZXNzIFByaW1hdGUgRGF0YQpgYGB7YmFzaH0KdXJsPWh0dHBzOi8vZnRwLm5jYmkubmxtLm5paC5nb3YvZ2VvL3Nlcmllcy9HU0UxMThubm4vR1NFMTE4NTQ2L3N1cHBsL0dTRTExODU0Nl9tYWNhcXVlX2ZvdmVhX2FsbF8xMFhfSmFuMjAxOC5SZGF0YS5negp3Z2V0ICR1cmwgLU8gcHJpbWF0ZV9kYXRhL0dTRTExODU0Nl9tYWNhcXVlX2ZvdmVhX2FsbF8xMFhfSmFuMjAxOC5SZGF0YS5negpndW56aXAgcHJpbWF0ZV9kYXRhLyoKYGBgCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCBjKCdkZXZ0b29scycsICdyb3h5Z2VuMicpICkKbGlicmFyeShkZXZ0b29scykKbGlicmFyeShyb3h5Z2VuMikKaW5zdGFsbF9naXRodWIoICdoYi1naXRpZmllZC9jZWxscmFuZ2VyUmtpdCcsCiAgICAgICAgICAgICAgICBhdXRoX3Rva2VuID0gJ3lvdXJfdG9rZW4nICkKYGBgCmBgYHtyfQpsb2FkKCJwcmltYXRlX2RhdGEvR1NFMTE4NTQ2X21hY2FxdWVfZm92ZWFfYWxsXzEwWF9KYW4yMDE4LlJkYXRhIikKCmRpbW5hbWVzKENvdW50Lm1hdF9mb3ZlYSlbWzFdXSA8LSB0b2xvd2VyKGRpbW5hbWVzKENvdW50Lm1hdF9mb3ZlYSlbWzFdXSkKbWFjYXF1ZV9mb3ZlYV9zZXVyYXQgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KENvdW50Lm1hdF9mb3ZlYSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2plY3QgPSAibWFjYXF1ZV9mb3ZlYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmNlbGxzID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZmVhdHVyZXMgPSAyMDApCgojIGdpdmUgbWFjYXF1ZSBkdGEgdW5pZm9ybSBuYW1lIGluICJvcmlnLmlkZW50IiBtZXRhZGF0YSBjb2x1bW4KQWRkTWV0YURhdGEobWFjYXF1ZV9mb3ZlYV9zZXVyYXQsIAogICAgICAgICAgICBtZXRhZGF0YSA9IG1hY2FxdWVfZm92ZWFfc2V1cmF0W1sib3JpZy5pZGVudCJdXSwgCiAgICAgICAgICAgIGNvbC5uYW1lID0gIm9yaWcuc2FtcGxlLm5hbWUiKQptYWNhcXVlX2ZvdmVhX3NldXJhdFtbIm9yaWcuaWRlbnQiXV0gPC0gIm1hY2FxdWVfZm92ZWEiCgptYWNhcXVlX2ZvdmVhX3NldXJhdApgYGAKQ2xlYW51cApgYGB7cn0Kcm0oaHVtYW4uY291bnRfbWF0cml4LCBodW1hbi5nZW5lX25hbWVzLCBodW1hbi5zYW1wbGVfYW5ub3RhdGlvbnMpCnJtKGNvdW50X21hdHJpeF9VUkwsIGdlbmVfbmFtZXNfVVJMLCBzYW1wbGVfYW5ub3RhdGlvbnNfVVJMLCBpbXBvcnRfcmVtb3RlX2RhdGEpCnJtKG1vdXNlLmRhdGEpCnJtKENvdW50Lm1hdF9mb3ZlYSwgbWFjYXF1ZV9mb3ZlYSkKYGBgCgoKQ29tYmluZQpgYGB7cn0KIyBjb21iaW5lCnJldC5saXN0IDwtIGxpc3QoaHVtYW4gPSBodW1hbl9yZXRfc2V1cmF0LCBtb3VzZSA9IG1vdXNlX3JldF9zZXVyYXQsIG1hY2FxdWUgPSBtYWNhcXVlX2ZvdmVhX3NldXJhdCkKCiMgcHJlcHJvY2VzcwpyZXQubGlzdCA8LSBsYXBwbHkoWCA9IHJldC5saXN0LCBGVU4gPSBmdW5jdGlvbih4KSB7CiAgICB4IDwtIE5vcm1hbGl6ZURhdGEoeCwgdmVyYm9zZSA9IEZBTFNFKQogICAgeCA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyh4LCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDIwMDAsIHZlcmJvc2UgPSBGQUxTRSkKfSkKCiMgY2xlYW51cApybShodW1hbl9yZXRfc2V1cmF0LCBtb3VzZV9yZXRfc2V1cmF0LCBtYWNhcXVlX2ZvdmVhX3NldXJhdCkKYGBgCgojIEludGVncmF0aW9uCmBgYHtyfQpwbGFuKCJtdWx0aXByb2Nlc3MiLCB3b3JrZXJzID0gNCkKcmV0LmFuY2hvcnMgPC0gRmluZEludGVncmF0aW9uQW5jaG9ycyhvYmplY3QubGlzdCA9IHJldC5saXN0LCBkaW1zID0gMTo1MCwgIGFuY2hvci5mZWF0dXJlcyA9IDEwMDApCnBsYW4oIm11bHRpcHJvY2VzcyIsIHdvcmtlcnMgPSAxKQpyZXQuY29tYmluZWQgPC0gSW50ZWdyYXRlRGF0YShhbmNob3JzZXQgPSByZXQuYW5jaG9ycywgZGltcyA9IDE6NTApCmBgYAoKIyBJbnRlZ3JhdGVkIEFuYWx5c2lzCmBgYHtyfQpwbGFuKCJtdWx0aXByb2Nlc3MiLCB3b3JrZXJzID0gNCkKCkRlZmF1bHRBc3NheShyZXQuY29tYmluZWQpIDwtICJpbnRlZ3JhdGVkIgoKIyBSdW4gdGhlIHN0YW5kYXJkIHdvcmtmbG93IGZvciB2aXN1YWxpemF0aW9uIGFuZCBjbHVzdGVyaW5nCnJldC5jb21iaW5lZCA8LSBTY2FsZURhdGEocmV0LmNvbWJpbmVkLCB2ZXJib3NlID0gRkFMU0UpCnJldC5jb21iaW5lZCA8LSBSdW5QQ0EocmV0LmNvbWJpbmVkLCBucGNzID0gNTAsIHZlcmJvc2UgPSBGQUxTRSkKIyB0LVNORSBhbmQgQ2x1c3RlcmluZwpyZXQuY29tYmluZWQgPC0gUnVuVU1BUChyZXQuY29tYmluZWQsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMTozNSkKcmV0LmNvbWJpbmVkIDwtIEZpbmROZWlnaGJvcnMocmV0LmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MzUpCnJldC5jb21iaW5lZCA8LSBGaW5kQ2x1c3RlcnMocmV0LmNvbWJpbmVkLCByZXNvbHV0aW9uID0gMC4wNzUpCmBgYAojIFVNQVAgVmlzdWFsaXphdGlvbgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpEaW1QbG90KHJldC5jb21iaW5lZCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikKRGltUGxvdChyZXQuY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFKQpgYGAKYGBge3IsIGZpZy5oZWlnaHQgPSA0LCBmaWcud2lkdGggPSAzfQpEaW1QbG90KHJldC5jb21iaW5lZCwgcmVkdWN0aW9uID0gInVtYXAiLCBzcGxpdC5ieSA9ICJvcmlnLmlkZW50IiwgbmNvbCA9IDEpCmBgYAoKIyBJZGVudGlmeSBDbHVzdGVycyB3aXRoIENhbm9uaWNhbCBNYXJrZXJzCmBgYHtyfQpEZWZhdWx0QXNzYXkocmV0LmNvbWJpbmVkKSA8LSAiUk5BIgoKZmVhdHVyZXMgPC0gdG9sb3dlcihjKCJQZGU2YSIsIkduYXQyIiwiTmVmbCIsIkNhbWsyYiIsIlRoeTEiLCJHYWQxIiwiU2xjNmE5IiwKICAgICAgICAgICAgICAgICAgICAgICJQY3NrNiIsIlRycG0xIiwiU2VwdDQiLCJHbHVsIiwiQXJyMyIsIkMxcWEiLCJUbTRzZjEiLCAiTWdwIikpCgpGZWF0dXJlUGxvdChvYmplY3QgPSByZXQuY29tYmluZWQsIAogICAgICAgICAgICBmZWF0dXJlcyA9IGZlYXR1cmVzLCAKICAgICAgICAgICAgcHQuc2l6ZSA9IDAuMSwKICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Z3JleSIsICIjRjI2OTY5IiksCiAgICAgICAgICAgIG1pbi5jdXRvZmYgPSAicTkiLAogICAgICAgICAgICBjb21iaW5lID0gVFJVRSkgJiBOb0xlZ2VuZCgpICYgTm9BeGVzKCkKYGBgCgoqIFJvZCA6IHBkZTZhCiogQUMgKGFtYWNyaW5lIGNlbGwpIDogZ2FkMSwgc2xjNmE5CiogTUcgKE3DvGxsZXIgZ2xpYSkgOiBnbHVsCiogQkMgKGJpcG9sYXIgY2VsbCkgOiBUcnBtLCBjYW1rMmIKKiBDQyAoY29uZSBjZWxsKSA6IGduYXQyLCBhcnIzCiogUkdDIChyZXRpbmFsIGdhbmdsaWFsIGNlbGwpIDogbmVmbCwgdGh5MQoqIFZDICh2YXNjdWxhciBjZWxsKSA6IG1ncCwgdG00c2YxCiogTSAobWljcm9nbGlhKSA6IGMxcWEKKiBIQyAoaG9yaXpvbnRhbCBjZWxsKSA6IHNlcHQ0CgpNYXJrZXJzIHdlcmUgZGV0ZXJtaW5lZCBmcm9tIFt0aGlzXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTQ2Ny0wMTktMTI3ODAtOCkgcGFwZXIgYW5kIG90aGVyIHNvdXJjZXMuCmBgYHtyfQpyZXQuY29tYmluZWQgPC0gUmVuYW1lSWRlbnRzKHJldC5jb21iaW5lZCwgYDBgID0gIk1HIiwgYDFgID0gIlJvZCIsIGAyYCA9ICJSR0MiLCAKICAgIGAzYCA9ICJSR0MiLCBgNGAgPSAiQkMiLCBgNWAgPSAiQ0MiLCBgNmAgPSAiQkMiLCBgN2AgPSAiQUMiLCBgOGAgPSAiQkMiLCBgOWAgPSAiUkdDIiwgCiAgICBgMTBgID0gIlJHQyIsIGAxMWA9ICJIQyIsIGAxMmAgPSAiTUciLCBgMTNgID0gIlZDIiwgYDE0YCA9ICJSR0MiLCBgMTVgID0gIlJHQyIsIGAxNmAgPSAiTSIsIGAxN2AgPSAiUkdDIikKCkRpbVBsb3QocmV0LmNvbWJpbmVkLCBsYWJlbCA9IFRSVUUpCmBgYAoKCiMgRmluZCBEaWZmZXJlbnRpYWxseSBFeHByZXNzZWQgR2VuZXMKYGBge3J9CmNlbGxzLnR5cGVzIDwtIGMoIlJvZCIsICJCQyIsICJNRyIsICJSR0MiLCAiQ0MiLCAiQUMiLCAiVkMiLCAiSEMiLCAiTSIpCnRoZW1lX3NldCh0aGVtZV9jb3dwbG90KCkpCgpjZWxsX3R5cGVfYXZnIDwtIGZ1bmN0aW9uKHNldXJhdC5jb21iaW5lZCwgaWRlbnQpIHsKICBjZWxscy54IDwtIHN1YnNldChzZXVyYXQuY29tYmluZWQsIGlkZW50cyA9IGlkZW50KQogIElkZW50cyhjZWxscy54KSA8LSAib3JpZy5pZGVudCIKICBjZWxscy54LmF2ZyA8LSBsb2cxcChBdmVyYWdlRXhwcmVzc2lvbihjZWxscy54LCB2ZXJib3NlID0gRkFMU0UpJFJOQSkKICBjZWxscy54LmF2ZyRnZW5lIDwtIHJvd25hbWVzKGNlbGxzLnguYXZnKQogIHJldHVybihjZWxscy54LmF2ZykKfQoKY2VsbHMucGxvdCA8LSBhcy5saXN0KGNlbGxzLnR5cGVzKQpjZWxscy5wbG90IDwtIGxhcHBseShjZWxscy5wbG90LCBGVU4gPSBmdW5jdGlvbih4KSB7CiAgY2VsbHMueC5hdmcgPC0gY2VsbF90eXBlX2F2ZyhyZXQuY29tYmluZWQsIGlkZW50ID0geCkKICB4IDwtIGdncGxvdChjZWxscy54LmF2ZywgYWVzKGh1bWFuX3JldCwgbW91c2VfcmV0KSkgKyBnZW9tX3BvaW50KHNpemUgPSAwLjEpICsgZ2d0aXRsZSh4KQogIHJldHVybih4KQp9KQoKIyBGb3IgaW5kaXZpZHVhbCBwbG90cwojIGZvciAocCBpbiBjZWxscy5wbG90KSB7CiMgICBwcmludChwKQojIH0KCiMgRm9yIGdyaWQgcGxvdApjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSBjZWxscy5wbG90LCBuY29sID0gMykKYGBgCmBgYHtyfQpyZXQuY29tYmluZWQkY2VsbHR5cGUub3JnYW5pc20gPC0gcGFzdGUoSWRlbnRzKHJldC5jb21iaW5lZCksIHJldC5jb21iaW5lZCRvcmlnLmlkZW50LCBzZXAgPSAiXyIpCnJldC5jb21iaW5lZCRjZWxsdHlwZSA8LSBJZGVudHMocmV0LmNvbWJpbmVkKQpJZGVudHMocmV0LmNvbWJpbmVkKSA8LSAiY2VsbHR5cGUub3JnYW5pc20iCmBgYApgYGB7cn0KY2VsbHMuZGlmZmdlbmVzIDwtIGFzLmxpc3QoY2VsbHMudHlwZXMpCmNlbGxzLmRpZmZnZW5lcyA8LSBsYXBwbHkoY2VsbHMuZGlmZmdlbmVzLCBGVU4gPSBmdW5jdGlvbih4KSB7CiAgbGFiX2h1bWFuIDwtIHNwcmludGYoIiVzX2h1bWFuX3JldCIsIHgpCiAgbGFiX21vdXNlIDwtIHNwcmludGYoIiVzX21vdXNlX3JldCIsIHgpCiAgcmV0dXJuKEZpbmRNYXJrZXJzKHJldC5jb21iaW5lZCwgaWRlbnQuMSA9IGxhYl9odW1hbiwgaWRlbnQuMiA9IGxhYl9tb3VzZSkpCn0pCgoKZm9yKGkgaW4gc2VxX2Fsb25nKGNlbGxzLmRpZmZnZW5lcykpIHsKCXggPC0gY2VsbHMuZGlmZmdlbmVzW1tpXV0KCXggPC0gY2JpbmQoeCwgbG9ncCA9IC1sb2coeCRwX3ZhbCksIHR5cGVzID0gY2VsbHMudHlwZXNbW2ldXSwgZ2VuZXMgPSByb3duYW1lcyh4KSkKCXggPC0geFshZ3JlcGwoIm10LSIsIHgkZ2VuZXMpLF0gIyByZW1vdmUgbWl0b2Nob25kcmlhbCBnZW5lcwoJY2VsbHMuZGlmZmdlbmVzW1tpXV0gPC0geAoJcm0oeCkKfQpgYGAKVGFibGVzIHdpdGggdGhlIG1vc3QgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGluIGVhY2ggY2VsbCBzdWJ0eXBlOgpgYGB7cn0KZm9yKGkgaW4gc2VxX2Fsb25nKGNlbGxzLmRpZmZnZW5lcykpIHsKICBwcmludChrbml0cjo6a2FibGUoaGVhZChjZWxscy5kaWZmZ2VuZXNbW2ldXSksY2FwdGlvbj1jZWxscy50eXBlc1tbaV1dKSkKfQpgYGAKU2F2ZSBhcyBjc3YgZmlsZXMKYGBge3J9CmZvcihpIGluIHNlcV9hbG9uZyhjZWxscy5kaWZmZ2VuZXMpKSB7CiAgd3JpdGUuY3N2KGNlbGxzLmRpZmZnZW5lc1tbaV1dLCBzcHJpbnRmKCJyZXN1bHRzLyVkXyVzLmNzdiIsIGksIGNlbGxzLnR5cGVzW1tpXV0pKQp9CmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KZ2VuZXNfdG9fcGxvdCA8LSAzCmZvciAoaSBpbiBzZXFfYWxvbmcoY2VsbHMudHlwZXMpKSB7CiAgcHJpbnQoRmVhdHVyZVBsb3Qob2JqZWN0ID0gcmV0LmNvbWJpbmVkLCAKICAgICAgICAgICAgICBmZWF0dXJlcyA9IHJvd25hbWVzKGNlbGxzLmRpZmZnZW5lc1tbaV1dKVsxOmdlbmVzX3RvX3Bsb3RdLCAKICAgICAgICAgICAgICBzcGxpdC5ieSA9ICJvcmlnLmlkZW50IiwgCiAgICAgICAgICAgICAgbWF4LmN1dG9mZiA9IDMsIAogICAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5IiwgInJlZCIpLAogICAgICAgICAgICAgIHB0LnNpemUgPSAwLjA3LAogICAgICAgICAgICAgIGNvbWJpbmUgPSBUUlVFLAogICAgICAgICAgICAgIGxhYmVsLnNpemUgPSAwLjUKICAgICAgICAgICAgICApICsgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gY2VsbHMudHlwZXNbW2ldXSkgJiBOb0xlZ2VuZCgpICYgTm9BeGVzKCkKICAgICAgICApCn0KYGBgCgpDaGVjayBjZWxsIHByb3BvcnRpb24gZm9yIGVhY2ggc3BlY2llczoKYGBge3J9CmtuaXRyOjprYWJsZShwcm9wLnRhYmxlKHggPSB0YWJsZShJZGVudHMocmV0LmNvbWJpbmVkKSwgcmV0LmNvbWJpbmVkQG1ldGEuZGF0YSRvcmlnLmlkZW50KSwgbWFyZ2luID0gMikpCmBgYAoKIyBHZW5lIEVucmljaG1lbnQgQW5hbHlzaXMKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShkYXRhLnRhYmxlKQpjZWxscy5kaWZmZ2VuZXMuY29tYmluZWQgPC0gcmJpbmRsaXN0KGNlbGxzLmRpZmZnZW5lcykKCiMgUHJlcHJvY2Vzc2luZwojIGZvcihpIGluIHNlcV9hbG9uZyhjZWxscy5kaWZmZ2VuZXMuY29tYmluZWQpKSB7CiMgCWlmIChjZWxscy5kaWZmZ2VuZXMuY29tYmluZWRbaV0kbG9ncCA+IDc1MCkgewojIAkJY2VsbHMuZGlmZmdlbmVzLmNvbWJpbmVkW2ldJGxvZ3AgPC0gNzQ5CiMgCX0KIyAJaWYgKGNlbGxzLmRpZmZnZW5lcy5jb21iaW5lZFtpXSRhdmdfbG9nRkMgPiAzKSB7CiMgCQljZWxscy5kaWZmZ2VuZXMuY29tYmluZWRbaV0kYXZnX2xvZ0ZDIDwtIDIuOTkKIyAJfQojIH0KIyAKIyBjZWxscy5kaWZmZ2VuZXMuY29tYmluZWQkbG9ncCA8LSBnc3ViKCJJbmYiLCA3NDksIGNlbGxzLmRpZmZnZW5lcy5jb21iaW5lZCRsb2dwKQoKZ2dwbG90KGRhdGE9Y2VsbHMuZGlmZmdlbmVzLmNvbWJpbmVkLCAKCQkgICBhZXMoeD1hdmdfbG9nRkMseT1sb2dwLCBjb2xvdXI9dHlwZXMsIGxhYmVsID0gZ2VuZXMpKSArIAoJZ2VvbV9wb2ludChzaXplPTAuMikgKyAKCXRoZW1lX2J3KCkgKyAKCXRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BKSwgCgkJICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksICAKCQkgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksIAoJCSAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCgkJICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArIAoJbGFicyh4ID0gImxvZzIoRm9sZCBjaGFuZ2VzKVxuKDNLL1dUKSIsIHkgPSItbG9nMTAocCB2YWx1ZSkiKSArCglzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzPWMoLTMsIDMpKSArCglzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMSwgNzUwKSkgKwoJZ2VvbV9obGluZSh5aW50ZXJjZXB0PSAxLCBjb2xvdXI9ImdyZXkiLCBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0wLjcgKSArCglnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9IDAgLCBjb2xvdXI9ImdyZXkiLCAgIHNpemU9MC43KQoKYGBgCgpgYGB7ciwgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDYsIGRwaSA9IDQwMCwgd2FybmluZz1GQUxTRX0KcGxvdF9lbnJpY2htZW50IDwtIGZ1bmN0aW9uKHR5cGUgPSAiUm9kIiwgb3RoZXIgPSAiIikgewoJZmlsZV9wYXRoIDwtIHNwcmludGYoImVucmljaF9kYXRhLyVzXyVzLnR4dCIsIHR5cGUsIG90aGVyKQoJeCA8LSByZWFkLnRhYmxlKGZpbGVfcGF0aCwgaGVhZGVyPVQsIHNlcD0iXHQiLCBza2lwID0gMTEpCgljb2xuYW1lcyh4KSA8LSBnc3ViKCJ1cGxvYWRfMS4uZm9sZC5FbnJpY2htZW50LiIsICJGb2xkX0VucmljaG1lbnQiLCBjb2xuYW1lcyh4KSkKCWNvbG5hbWVzKHgpIDwtIGdzdWIoInVwbG9hZF8xLi5GRFIuIiwgIkZEUiIsIGNvbG5hbWVzKHgpKQoJY29sbmFtZXMoeCkgPC0gZ3N1YigiR08uYmlvbG9naWNhbC5wcm9jZXNzLmNvbXBsZXRlIiwgIkdPIiwgY29sbmFtZXMoeCkpCgljb2xuYW1lcyh4KSA8LSBnc3ViKCJIb21vLnNhcGllbnMuLi5SRUZMSVNULi4yMDg1MS4iLCAiQ291bnQiLCBjb2xuYW1lcyh4KSkKCXgkR088LSBmYWN0b3IoeCRHTywgbGV2ZWxzID0geCRHT1tvcmRlcih4JEZvbGRfRW5yaWNobWVudCwgZGVjcmVhc2luZyA9RildKQoJeCA8LSB4W29yZGVyKHgkRkRSKSxdCgl4IDwtIHhbMToxMCxdCglnPC0gZ2dwbG90KGRhdGE9eCwgYWVzKHg9Rm9sZF9FbnJpY2htZW50LCB5PUdPLCBjb2xvdXI9RkRSKSkgKyAKCQlnZW9tX3BvaW50KGFlcyhzaXplPUNvdW50KSkgKyAKCQl0aGVtZV9idygpICsKCQl0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSkgLCAKCQkJICBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLCAKCQkJICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpICwgCgkJCSAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCgkJCSAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKCQlsYWJzKHggPSAiICIsIHkgPSIgIikgKwoJCXNjYWxlX2NvbG91cl9ncmFkaWVudChsb3cgPSAicmVkIiwgaGlnaCA9ICJibHVlIikgKwoJCWdndGl0bGUodHlwZSkKCXJldHVybihnKQp9Cgpmb3IgKHR5cGUgaW4gY2VsbHMudHlwZXMpIHsKCXByaW50KHBsb3RfZW5yaWNobWVudCh0eXBlID0gdHlwZSwgb3RoZXI9ICJ0b3AyMDAiKSkKCXJtKHR5cGUpCn0KYGBgCgo=